package com.joyoung.sdk.utils;

import org.apache.commons.codec.binary.Hex;
import org.dom4j.DocumentException;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.joyoung.sdk.config.Cons_Sdk;
import com.joyoung.sdk.info.BaseRequesRes;
import com.joyoung.sdk.info.UserConfirmData;
import com.joyoung.sdk.interface_sdk.CallBack;
import com.joyoung.sdk.interface_sdk.ChangeDevCallBack;
import com.joyoung.sdk.interface_sdk.CommandCallBack;
import com.joyoung.sdk.interface_sdk.SendCmdCallback;
import com.joyoung.sdk.interface_sdk.JoyoungLinkCallBack;
import com.joyoung.sdk.resolve.ResolveCommand;
import com.joyoung.sdk.service.JoyounglinkServers;
import com.joyoung.sdk.utils.encryptdecryptutil.Encrypt;
import com.joyoung.sdk.utils.encryptdecryptutil.XXTea;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;

import java.io.IOException;
import java.util.ArrayList;

import okhttp3.Call;
import okhttp3.Response;

/**
 * Created by liuwei on 2016/7/1.
 */
public final class JoyoungSDK {

    public static final String TAG = JoyoungSDK.class.getSimpleName();
    private Context mContext;
    private CommandCallBack mCommandCallBack;
    private ResolveCommand resolveCommand;
    private String devID;
    private String devTypeId;
    private String source;
    private JoyoungLinkCallBack joyoungLinkCallBack;
    private SendCmdCallback sendCmdCallback;
    private boolean isSubscription = false;


    private static JoyoungSDK joyoungSDK = null;

    private JoyoungSDK() {
    }

    /***
     * 单例模式，保证只有一个实例对象
     * @return JoyoungSDK
     */
    synchronized static JoyoungSDK getInstance() {
        if (joyoungSDK == null) {
            joyoungSDK = new JoyoungSDK();
        }
        return joyoungSDK;
    }


    /***
     *
     * @param context         上下文
     * @param commandCallBack MQTT返回信息的回调
     */
    private void connectMqtt(Context context, CommandCallBack commandCallBack) {
        this.mCommandCallBack = commandCallBack;
        this.mContext = context;
        start_service();
    }


    /***
     * 初始化sdk
     * @param context 上下文
     * @param logindata 登录成功返回的数据
     * @param phonenumber 手机号
     * @param passwd 密码
     * @param source app类型，1代表爱下厨，2代表空净、3代表ONECUP
     * @param commandCallBack  指令返回信息的回调
     * @param joyoungLinkCallBack 连接成功回调，在成功的方法里可以切换设备
     */
      void init(Context context, String logindata, String phonenumber, String passwd, String source, CommandCallBack commandCallBack, JoyoungLinkCallBack joyoungLinkCallBack) {
        if (context == null || logindata == null || phonenumber == null || passwd == null || source == null || commandCallBack == null || joyoungLinkCallBack == null) {
            throw new NullPointerException("参数为空");
        }
        String xxteaKey = Encrypt.MD5(phonenumber + passwd).substring(0, 16);
        initKey(logindata, xxteaKey);
        this.source = source;
        this.joyoungLinkCallBack = joyoungLinkCallBack;
        connectMqtt(context, commandCallBack);
    }


    private void initKey(String loginData, String xxteaKey) {
        Gson gson = new Gson();
        BaseRequesRes<UserConfirmData> baseRequesRes = gson.fromJson(loginData,
                new TypeToken<BaseRequesRes<UserConfirmData>>() {
                }.getType());
        if (null != baseRequesRes && baseRequesRes.getCode() == 0) {
            try {
                //登录成功后把从服务器得到的AccessToken解码之后得到accessToken2
                byte[] accessToken2 = Hex.decodeHex(baseRequesRes.getData().getAccessToken().toCharArray());

                //把accessToken用XXTea解密得到sessionKey （必须传）
                String sessionKey = new String(XXTea.decrypt(accessToken2, xxteaKey.getBytes()));
                Cons_Sdk.sessionkey = sessionKey;

                //sessionId 是直接从服务器返回的数据得到的（必须传）
                Cons_Sdk.sessionId = baseRequesRes.getData().getSessionId();

                //dataKey 是sessionKey通过取奇数位得到的是sdk内部用来获取长连接地址的（必须传）
                String dataKey = "";
                for (int i = 0; i < sessionKey.length(); i++) {
                    if (i % 2 == 0) {
                        dataKey += sessionKey.substring(i, i + 1);
                    }
                }
                Cons_Sdk.dataKey = dataKey;
            } catch (Exception e) {

            }
        }
    }

    /***
     * 初始化需要sdk内部解析指令，生成json字符串的调用本方法，注意：当isResolve==true时，必须配置configXmlName
     * @param context 上下文
     * @param logindata 登录成功返回的数据
     * @param phonenumber 手机号
     * @param passwd 密码
     * @param source app类型，1代表爱下厨，2代表空净、3代表ONECUP
     * @param isResolve 是否需要解析指令
     * @param configXmlName 指令结构的key
     * @param commandCallBack 返回信息的回调
     * @param joyoungLinkCallBack 连接成功回调，在成功的方法里可以切换设备
     */
    void init(Context context, String logindata, String phonenumber, String passwd, String source, boolean isResolve, String configXmlName, CommandCallBack commandCallBack, JoyoungLinkCallBack joyoungLinkCallBack) {
        if (context == null || logindata == null || phonenumber == null || passwd == null || source == null || configXmlName == null || commandCallBack == null || joyoungLinkCallBack == null) {
            throw new NullPointerException("参数为空");
        }
        String xxteaKey = Encrypt.MD5(phonenumber + passwd).substring(0, 16);
        initKey(logindata, xxteaKey);
        this.source = source;
        this.joyoungLinkCallBack = joyoungLinkCallBack;
        if (configXmlName == null) {
            Cons_Sdk.isResolve = false;
        } else {
            Cons_Sdk.isResolve = isResolve;
            Cons_Sdk.configXmlName = configXmlName;
        }
        connectMqtt(context, commandCallBack);
    }

    private CallBack callBack = new CallBack() {
        @Override
        public void onSuccess() {
            joyoungLinkCallBack.onSuccess();
        }

        @Override
        public void onError() {
            joyoungLinkCallBack.onError();
            if (mContext != null && commandCallBack != null && callBack != null) {
                connectMqtt(mContext, commandCallBack);
            }
        }
    };


    /***
     *发送指令
     * @param cmd 指令，如查询指令（cc00000100000700300100b20000）
     * @param isNew  是否新架构，不是传false，是传true
     * @param sendCmdCallback 发送命令的成功或失败回调
     * @throws Exception
     */
    void sendCMD(String cmd, String devID, boolean isNew, SendCmdCallback sendCmdCallback) throws Exception {
        if (cmd == null || devID == null)
            return;
        if (isNew) {
            newSendCMD(cmd, devID, sendCmdCallback);
        } else {
            oldSendCMD(cmd, "0", devID, sendCmdCallback);
        }
    }

    /***
     *新架构发送指令
     * @param cmd 指令，如查询指令（cc00000100000700300100b20000）
     * @param devID 设备ID
     * @param sendCmdCallback 回调
     * @throws Exception
     */
    void sendCMD(String cmd, String devID, SendCmdCallback sendCmdCallback) throws Exception {
        if (cmd == null || devID == null)
            return;
        newSendCMD(cmd, devID, sendCmdCallback);
    }

    /***
     * 新架构发送指令
     *
     * @param cmd      指令，如查询指令（cc00000100000700300100b20000）
     * @param devID    设备ID
     * @param sendCmdCallback 发送命令的成功或失败回调
     * @throws Exception
     */
    private void newSendCMD(String cmd, String devID, SendCmdCallback sendCmdCallback) throws Exception {
        this.sendCmdCallback = sendCmdCallback;
        JSONObject object = new JSONObject();
        object.put("devId", devID);
        Log.e("设备ID", devID);
        object.put("cmd", cmd);
        String url = URLGenerateUtil.urlGenerateCMS(Cons_Sdk.BASECONTRALURL, object.toString());
        byte[] xxtea = XXTea.encrypt(object.toString().getBytes(), Cons_Sdk.dataKey.getBytes("utf-8"));
        Log.e("发送指令mContext是否为空", mContext + "");
        Log.e("发送指令", cmd);
        JyHttp.getInstance().sendCMD(mContext, url, Cons_Sdk.sslName, xxtea, source, okhttpcallback);
    }


    private okhttp3.Callback okhttpcallback = new okhttp3.Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            try {
                sendCmdCallback.onFailure(call.execute().message(), e);
            } catch (IOException e1) {
                e1.printStackTrace();
                sendCmdCallback.onFailure("error", e);
            }
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            sendCmdCallback.onResponse(response.body().string());
        }
    };

    /***
     * 老架构发送指令
     *
     * @param cmd   指令，如查询指令（cc00000100000700300100b20000）
     * @param devID 设备ID
     * @param sendCmdCallback 发送命令的成功或失败回调
     * @throws Exception
     */
    private void oldSendCMD(String cmd, String isNew, String devID, SendCmdCallback sendCmdCallback) throws Exception {
        this.sendCmdCallback = sendCmdCallback;
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("devId", devID);
        Log.e("设备ID", devID);
        jsonObject.put("isNew", isNew);
        jsonObject.put("cmd", cmd);
        String url = URLGenerateUtil.urlGenerateCMS("/cms/v1/app/controlDev", jsonObject.toString());
        byte[] xxtea = XXTea.encrypt(jsonObject.toString().getBytes(), Cons_Sdk.dataKey.getBytes("utf-8"));
        Log.e("发送指令mContext是否为空", mContext + "");
        Log.e("发送指令", cmd);
        JyHttp.getInstance().sendCMD(this.mContext, url, Cons_Sdk.sslName, xxtea, source, okhttpcallback);
    }

    private void start_service() {
        Log.e("start_service", "start_service-----" + mContext);
        Intent intent = new Intent(mContext, JoyounglinkServers.class);
        mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private JoyounglinkServers servers;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            servers = ((JoyounglinkServers.MyBind) service).getServers();
            servers.setOnMqttCallback(commandCallBack);
            servers.setCallback(callBack);
            Log.e("msg", "服务开启");
            if (isSubscription && devID != null && devTypeId != null && mContext != null && mCommandCallBack != null && callBack != null) {
                subscription(devTypeId, devID);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            servers = null;
            Log.e("msg", "服务停止");
        }
    };

    private CommandCallBack commandCallBack = new CommandCallBack() {
        @Override
        public void connectionLost(String msg) {
            mCommandCallBack.connectionLost(msg);
        }

        @Override
        public void messageArrived(String topic, String msg) {
            Log.e("mqttservers返回", "topic=" + topic + ",msg=" + msg);
            if (Cons_Sdk.isResolve) {
                try {
                    JSONObject jsonObject = new JSONObject(msg);
                    String data = jsonObject.optString("data");
                    Log.e("接受的数据", data);
                    ArrayList<String> cmdArray = DataUtils.bytesToHexStringArray2(data);
                    if (resolveCommand == null)
                        resolveCommand = new ResolveCommand();
                    JSONObject dataObject = resolveCommand.resolve(mContext, cmdArray);
                    Log.e("指令解析返回的data", dataObject.toString());
                    JSONObject json = new JSONObject();
                    json.put("data", dataObject);
                    json.put("devId", jsonObject.optString("devId"));
                    json.put("funName", jsonObject.optString("funName"));
                    json.put("happenTime", jsonObject.optString("happenTime"));
                    mCommandCallBack.messageArrived(topic, json.toString());
                } catch (JSONException e) {
                    Log.e("指令解析返回的JSONException", e.getMessage());
                    mCommandCallBack.messageArrived(topic, msg);
                } catch (DocumentException e) {
                    Log.e("指令解析返回DocumentException", e.getMessage());
                    mCommandCallBack.messageArrived(topic, msg);
                } catch (IOException e) {
                    Log.e("指令解析返回的IOException", e.getMessage());
                    mCommandCallBack.messageArrived(topic, msg);
                }
            } else {
                mCommandCallBack.messageArrived(topic, msg);
            }


        }

        @Override
        public void deliveryComplete(String token) {
            mCommandCallBack.deliveryComplete(token);
        }
    };

    /***
     * 订阅主题
     *
     * @param devTypeId 设备型号，如（12289代表料理机Y18，15361代表烤箱35I6）
     * @param devID     设备ID
     */
    private void subscription(String devTypeId, String devID) {
        Log.e("servers", servers + "--------------" + devTypeId + "------" + devID);
        if (servers == null) {
            isSubscription = true;
            start_service();
        } else {

            servers.subscription(devTypeId, devID);
        }

    }


    /***
     * 切换设备
     * @param context 上下文
     * @param devTypeId 设备型号，如（12289代表料理机Y18，15361代表烤箱35I6）
     * @param devID 设备ID
     * @param commandCallBack 连接返回信息的回调
     * @param changeDevCallBack 切换设备成功的回调
     */
    void changeDev(Context context, String decryptKey, String devTypeId, String devID, CommandCallBack commandCallBack, ChangeDevCallBack changeDevCallBack) {
        Cons_Sdk.decryptKey = decryptKey;
        unSubscription();
        if (mCommandCallBack == null)
            this.mCommandCallBack = commandCallBack;
        if (mContext == null)
            this.mContext = context;

        this.devID = devID;
        this.devTypeId = devTypeId;
        Log.e("servers", servers + "--------------" + devTypeId + "------" + devID);
        servers.setChangeDevCallBack(changeDevCallBack);
        subscription(devTypeId, devID);
    }

    /***
     * 解除订阅
     */
    private void unSubscription() {
        if (servers != null) {
            servers.unSubscription();
            Log.e("解除订阅", servers + "");
        }
    }


    /***
     * 服务停止
     * 解除订阅
     */
    void onDestroy() {
        if (mContext != null && connection != null) {
            mContext.unbindService(connection);
        }
        joyoungSDK = null;
        System.gc();
    }

}
